﻿using Humanizer;
using Msp.Scaffolding.Entities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using ProjectInfo = Msp.Scaffolding.Entities.ProjectInfo;

namespace Msp.Scaffolding
{
    public class App
    {
        private readonly ILogger<App> _logger;
        private readonly SettingOptions _settingOptions;
        public App(ILogger<App> logger, IOptionsMonitor<SettingOptions> settingOptions)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _settingOptions = settingOptions.CurrentValue;
        }




        public async Task RunAsync(string[] args)
        {

            //Tool tool = new Tool();
            //tool.Get();

            //return;
        
           // var basepath = AppContext.BaseDirectory.Substring(0, AppContext.BaseDirectory.IndexOf("\\Net"));

            _settingOptions.TemplatePath = "./Templates";




            Console.WriteLine("请输入ProjectName:如 Application,如果为空则为如Host.Application");
            _settingOptions.ProjectName = Console.ReadLine();
            if (string.IsNullOrWhiteSpace(_settingOptions.ProjectName))
            {
                _settingOptions.ProjectName = "Host.Application";
            }

            Console.WriteLine("请输入ModuleName:如Wms，取应用名下的文件夹名");
            _settingOptions.ModuleName = Console.ReadLine();
            if (string.IsNullOrWhiteSpace(_settingOptions.ModuleName))
            {
                Console.WriteLine($"模块名不能为空~");
                Console.ReadKey();
                Environment.Exit(0);
            }


            //D:\project\会员系统\MBS\MBS\templete\Li\Src\Li.Entities\
            Console.WriteLine(@$"请输入应用目录,Application.Entities文件所在目录");
            var path = Console.ReadLine();

            if (!Directory.Exists(path))
            {
                Console.WriteLine($"目录路径不存在{path}~");
                Console.ReadKey();
                Environment.Exit(0);
            }





            //D:\\project\\会员系统\\MBS\\MBS\\templete\\Li\\Src\\Li.Entities\\bin\\Debug\\net6.0\\Li.Entities.xml
            Console.WriteLine($"请输入XmlFile路径");
            _settingOptions.XmlFile = Console.ReadLine();
            if (!File.Exists(_settingOptions.XmlFile))
            {
                Console.WriteLine("XmlFile不存在");
                Console.ReadKey();
                Environment.Exit(0);    
            }
            Console.WriteLine("请输入OutputDirectory路径，如果为空，则为当前目录的out");

            _settingOptions.OutputDirectory = Console.ReadLine();
            if (string.IsNullOrWhiteSpace(_settingOptions.OutputDirectory))
            {
                _settingOptions.OutputDirectory = AppContext.BaseDirectory + "OUT";
            }

            if (!Directory.Exists(_settingOptions.OutputDirectory))
            {
                Directory.CreateDirectory(_settingOptions.OutputDirectory);
            }


            Console.WriteLine("开始...");

            if (System.IO.Directory.Exists(path))
            {
                var files = System.IO.Directory.GetFiles(path);

                foreach (var item in files)
                {
                    Console.WriteLine($"开始生成文件{item}");
                    await GenFile(item);
                    Console.WriteLine($"{item}文件生成完毕");
                }
            }

            //  _logger.LogInformation($"生成完毕，目录：  {_settingOptions.OutputDirectory}");
            Console.WriteLine($"生成完毕，目录：  {_settingOptions.OutputDirectory}");
            Console.ReadKey();
            Environment.Exit(0);
        }


        private async Task GenFile(string entityPath)
        {

            try
            {
                ProjectInfo projectInfo = ProjectParser();

                // string entityPath = Path.Combine(_settingOptions.BaseDirectory, _settingOptions.EntityFilePath);
                EntityInfo entityInfo = EntityParse(entityPath, projectInfo);
                if (entityInfo != null)
                {
                    var model = new
                    {
                        ProjectInfo = projectInfo,
                        EntityInfo = entityInfo,
                        Option = new CommandOption()
                        {
                            CustomRepository = false,
                        },
                        SettingOptions = _settingOptions
                    };
                    #region 参考值
                    /*
                        -   ProjectInfo	{Li.Scaffolding.Entities.ProjectInfo}	Li.Scaffolding.Entities.ProjectInfo
                            BaseDirectory	"D:/code/github/lin-cms-dotnetcore/src/CMS.Core/"	string
                            FullName	"PopCloud"	string
                            Name	"PopCloud"	string

                        -   EntityInfo
                            BaseType	"FullAduitEntity"	string
                            Name	"Doc"	string		
                            NameCamelize	"doc"	string
                            NamePluralized	"Docs"	string
                            Namespace	"CMS.v1.Docs"	string
                            NamespaceLastPart	"Docs"	string
                            PrimaryKey	"long"	string
                            RelativeDirectory	"v1/Docs"	string
                            RelativeNamespace	"v1.Docs"	string

                        -   SettingOptions	{Li.Scaffolding.SettingOptions}	Li.Scaffolding.SettingOptions
                            Areas	"Base"	string
                            AreasCamelize	"base"	string
                            BaseDirectory	"D:/code/github/lin-cms-dotnetcore/src/CMS.Core/"	string
                            EntityFilePath	"Entities/Doc.cs"	string
                            OutputDirectory	"D:/code/github/Outputs"	string
                            ProjectName	"PopCloud"	string
                            TemplatePath	"./Templates"	string

                    */
                    #endregion
                    string templatePath = Path.Combine(Environment.CurrentDirectory, _settingOptions.TemplatePath);
                    CodeScaffolding codeScaffolding = new CodeScaffolding(templatePath, _settingOptions.OutputDirectory, _logger);
                    await codeScaffolding.GenerateAsync(model);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("失败：" + entityPath);
                //throw ex;
            }


        }


        private ProjectInfo ProjectParser()
        {
            _logger.LogInformation($"baseDirectory：{_settingOptions.BaseDirectory}"); ;
            //   string coreCsprojFile = Directory.EnumerateFiles(_settingOptions.BaseDirectory, "*.Core.csproj", SearchOption.AllDirectories).FirstOrDefault();

            // string fileName = Path.GetFileName(coreCsprojFile);
            ProjectInfo projectInfo = new ProjectInfo(_settingOptions.BaseDirectory, _settingOptions.ModuleName, _settingOptions.ProjectName);

            return projectInfo;
        }

        private EntityInfo EntityParse(string entityFilePath, ProjectInfo projectInfo)
        {
            string sourceText = File.ReadAllText(entityFilePath);


            SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceText);

            CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
            string @namespace = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>().Single().Name.ToString();//不满足项目命名空间
            ClassDeclarationSyntax classDeclarationSyntax = root.DescendantNodes().OfType<ClassDeclarationSyntax>()
                .Where(x => x.AttributeLists.Any(z => z.ToFullString().Contains("Table"))).FirstOrDefault();

            if (classDeclarationSyntax == null)
            {
                return null;
            }
            string className = classDeclarationSyntax.Identifier.ToString();




            BaseListSyntax baseList = classDeclarationSyntax.BaseList;

            if (baseList == null)
            {
                _logger.LogError($"{entityFilePath}文件没继承类");
                //  return null;
            }

            GenericNameSyntax genericNameSyntax = baseList?.DescendantNodes().OfType<SimpleBaseTypeSyntax>()
                .First(node => !node.ToFullString().StartsWith("I")) // Not interface
                .DescendantNodes().OfType<GenericNameSyntax>()
                .FirstOrDefault();

            string baseType;
            string primaryKey;
            List<string> interfaces = new List<string>();
            if (genericNameSyntax == null)
            {
                // No generic parameter -> Entity with Composite Keys
                baseType = baseList?.DescendantNodes().OfType<SimpleBaseTypeSyntax>()
                    .First(x => !x.ToFullString().StartsWith("I")).Type.ToString();

                var inters = baseList?.DescendantNodes().OfType<SimpleBaseTypeSyntax>()
                   .Where(x => x.ToFullString().StartsWith("I"));

                if (inters != null)
                {
                    foreach (var item in inters)
                    {
                        interfaces.Add(item.Type.ToFullString().Trim());
                    }
                }


                primaryKey = _settingOptions.DefaultPrimaryKeyType;//   "long";

            }
            else
            {
                // Normal entity
                baseType = genericNameSyntax.Identifier.ToString();
                primaryKey = genericNameSyntax.DescendantNodes().OfType<TypeArgumentListSyntax>().Single().Arguments[0].ToString();
            }
            List<PropertyInfo> properties = root.DescendantNodes().OfType<PropertyDeclarationSyntax>()
                .Where(x => !x.ToFullString().Contains("TenantId"))
                  .Select(prop =>
                  {
                      var p = new PropertyInfo(prop.Type.ToString(), prop.Identifier.Value.ToString());
                      p.FullName = prop.ToFullString();
                      if (prop.Type.Kind().ToString().Contains("NullableType"))
                      {
                          p.HasNullable = true;
                      }

                      foreach (AttributeListSyntax att in prop.AttributeLists)
                      {
                          foreach (AttributeSyntax aa in att.Attributes)
                          {

                              var ppname = aa.Name.NormalizeWhitespace().ToFullString();
                              //获取注解，到时候可以根据类的注解来生成代码，比如验证长度，字段是否必需。
                              if (ppname.EndsWith("Field")
                              || ppname.EndsWith("FieldAttribute"))
                              {
                                  if (aa.ArgumentList.Arguments.Count > 0)
                                  {
                                      foreach (var item in aa.ArgumentList.Arguments)
                                      {
                                          var a1 = item.ToString();
                                          setField(a1, p);
                                      }

                                  }
                              }

                              if (ppname.EndsWith("Display"))
                              {

                                  //var pname = aa.ArgumentList.Arguments.First().NameEquals?.Name.Identifier.ValueText;
                                  //var pvalue = aa.ArgumentList.Arguments.First().Expression.NormalizeWhitespace().ToFullString();


                                  SetDisplay(aa, p);
                              }
                              if (ppname.EndsWith("MaxLength"))
                              {
                                  SetMaxLength(aa, p);
                              }
                              if (ppname.EndsWith("StringLength"))
                              {
                                  SetStringLength(aa, p);
                              }
                              if (ppname.EndsWith("Required"))
                              {
                                  SetRequired(aa, p);
                              }
                              if (ppname.EndsWith("RegularExpression"))
                              {
                                  SetRegularExpression(aa, p);
                              }
                              if (ppname.EndsWith("Compare"))
                              {
                                  SetCompare(aa, p);
                              }
                              if (ppname.EndsWith("MinLength"))
                              {
                                  SetMinLength(aa, p);
                              }
                              if (ppname.EndsWith("Remote"))
                              {
                                  SetRemote(aa, p);
                              }
                              if (ppname.EndsWith("DataType"))
                              {
                                  SetDataType(aa, p);
                              }
                              if (ppname.EndsWith("Range"))
                              {
                                  SetRange(aa, p);
                              }



                          }
                      }
                      return p;
                  })
                  .ToList();

            #region MyRegion
            void setField(string a, PropertyInfo p)
            {

                var am = a.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);

                if (am.Length > 1)
                {
                    if (am[0].Contains("Type"))
                    {
                        p.InputType = $"{am[1].Replace("{0}", "").Replace("FieldType.", "").Replace("{1}", "").Replace("FieldType.", "").Replace("\"", "").Trim()}";//string.Format(am[1], p.Name);
                    }
                    else if (am[0].Contains("Format"))
                    {
                        p.Format = $"{am[1].Replace("{0}", "").Replace("FieldType.", "").Replace("{1}", "").Replace("FieldType.", "").Replace("\"", "").Trim()}";//string.Format(am[1], p.Name);
                    }
                }
            }
            void SetRange(AttributeSyntax aa, PropertyInfo p)
            {


                if (aa.ArgumentList.Arguments.Count > 0)
                {
                    int.TryParse(aa.ArgumentList.Arguments[0].ToString(), out int m);
                    p.Range_Min = m;
                }
                if (aa.ArgumentList.Arguments.Count > 1)
                {

                    int.TryParse(aa.ArgumentList.Arguments[1].ToString(), out int m);
                    p.Range_Max = m;

                }
                if (aa.ArgumentList.Arguments.Count > 2)
                {
                    var a1 = aa.ArgumentList.Arguments[2].ToString();
                    //ErrorMessage ="{0}的长度必须在10位以内"
                    var am = a1.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                    if (am != null && am.Length > 1)
                    {
                        p.Range_Msg = $"大小必须{p.Range_Min.GetValueOrDefault()}至{p.Range_Max.GetValueOrDefault()}之间";//string.Format(am[1], p.Name);
                    }

                }
            }

            void SetDisplay(AttributeSyntax aa, PropertyInfo p)
            {

                if (aa.ArgumentList.Arguments.Count > 0)
                {
                    var a1 = aa.ArgumentList.Arguments[0].ToString();
                    var am = a1.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                    if (am.Length > 1) p.DisplayName = $"{am[1].Replace("{0}", "").Replace("{1}", "").Replace("\"", "").Trim()}";//string.Format(am[1], p.Name);
                }
            }

            void SetDataType(AttributeSyntax aa, PropertyInfo p)
            {
                if (aa.ArgumentList.Arguments.Count > 0)
                {
                    p.DataType = aa.ArgumentList.Arguments[0].ToString();
                    if (p.DataType.Contains("."))
                    {
                        var aar = p.DataType.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                        p.DataType = aar[aar.Length - 1];
                    }
                }
                if (aa.ArgumentList.Arguments.Count > 1)
                {
                    var a1 = aa.ArgumentList.Arguments[1].ToString();
                    var am = a1.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                    if (am.Length > 1) p.DataType_Msg = $"{am[1].Replace("{0}", "").Replace("{1}", "").Replace("\"", "").Trim()}";//string.Format(am[1], p.Name);
                }
            }


            void SetRemote(AttributeSyntax aa, PropertyInfo p)
            {
                if (aa.ArgumentList.Arguments.Count > 0)
                {
                    p.Remote = aa.ArgumentList.Arguments[0].ToString();
                }
                if (aa.ArgumentList.Arguments.Count > 1)
                {
                    var a1 = aa.ArgumentList.Arguments[1].ToString();
                    var am = a1.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                    if (am.Length > 1) p.Remote_msg = $"{am[1].Replace("{0}", "").Replace("{1}", "").Replace("\"", "").Trim()}";//string.Format(am[1], p.Name);
                }
            }


            void SetMinLength(AttributeSyntax aa, PropertyInfo p)
            {
                if (aa.ArgumentList.Arguments.Count > 0)
                {
                    int.TryParse(aa.ArgumentList.Arguments[0].ToString(), out int m);
                    p.MinLength = m;
                }
                if (aa.ArgumentList.Arguments.Count > 1)
                {
                    var a1 = aa.ArgumentList.Arguments[1].ToString();
                    var am = a1.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                    p.MinLength_Msg = $"至少输入{p.MaxLength.GetValueOrDefault()}个字符";//string.Format(am[1], p.Name);
                }
            }



            void SetCompare(AttributeSyntax aa, PropertyInfo p)
            {
                if (aa.ArgumentList.Arguments.Count > 0)
                {
                    p.Compare = aa.ArgumentList.Arguments[0].ToString();
                }
                if (aa.ArgumentList.Arguments.Count > 1)
                {
                    var a1 = aa.ArgumentList.Arguments[1].ToString();
                    var am = a1.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                    p.Compare_Msg = $"{am[1].Replace("{0}", "").Replace("{1}", "").Replace("\"", "").Trim()}";//string.Format(am[1], p.Name);
                }
            }



            void SetRegularExpression(AttributeSyntax aa, PropertyInfo p)
            {
                if (aa.ArgumentList.Arguments.Count > 0)
                {
                    p.RegularExpression = aa.ArgumentList.Arguments[0].ToString();
                }
                if (aa.ArgumentList.Arguments.Count > 1)
                {
                    var a1 = aa.ArgumentList.Arguments[1].ToString();
                    var am = a1.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                    p.RegularExpressionMsg = $"{am[1].Replace("{0}", "").Replace("{1}", "").Replace("\"", "").Trim()}";//string.Format(am[1], p.Name);
                }
            }





            void SetRequired(AttributeSyntax aa, PropertyInfo p)
            {
                p.Required = true;
            }




            void SetStringLength(AttributeSyntax aa, PropertyInfo p)
            {
                if (aa.ArgumentList.Arguments.Count > 0)
                {
                    int.TryParse(aa.ArgumentList.Arguments[0].ToString(), out int m);
                    p.StringLength_Max = m;
                }
                if (aa.ArgumentList.Arguments.Count > 1)
                {
                    var a1 = aa.ArgumentList.Arguments[1].ToString();
                    //ErrorMessage ="{0}的长度必须在10位以内"
                    if (a1.StartsWith("MinimumLength"))
                    {
                        var am = a1.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                        if (am != null && am.Length > 1)
                        {
                            int.TryParse(am[1], out int m1);
                            p.StringLength_Min = m1;
                            p.StringLength_Msg = $"{p.Remarks}只能在{p.StringLength_Min.GetValueOrDefault()}至{p.StringLength_Max.GetValueOrDefault()}个字符以内";//string.Format(am[1], p.Name);
                        }
                    }
                }
            }


            void SetMaxLength(AttributeSyntax aa, PropertyInfo p)
            {
                if (aa.ArgumentList.Arguments.Count > 0)
                {
                    int.TryParse(aa.ArgumentList.Arguments[0].ToString(), out int m);
                    p.MaxLength = m;
                }
                if (aa.ArgumentList.Arguments.Count > 1)
                {
                    var a1 = aa.ArgumentList.Arguments[1].ToString();
                    var am = a1.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                    p.MaxLengthMsg = $"{p.Remarks}最多只允许输入{p.MaxLength.GetValueOrDefault()}个字符";//string.Format(am[1], p.Name);
                }
            }
            #endregion




            string xmlPath = _settingOptions.XmlFile;// _settingOptions.BaseDirectory + projectInfo.FullName + ".Core.xml";
            string entityRemark = Util.GetEntityRemarkBySummary(xmlPath, properties, @namespace + "." + className);


            if (_settingOptions.Areas != null)
            {
                @namespace = projectInfo.ModuleName + "." + _settingOptions.Areas + "." + className.Pluralize();
            }
            else
            {
                @namespace = projectInfo.ModuleName + "." + className.Pluralize();
            }
            string relativeDirectory = @namespace.RemovePreFix(projectInfo.ModuleName + ".").Replace('.', '/');

            EntityInfo entityInfo = new EntityInfo(@namespace, className, baseType, primaryKey, relativeDirectory);
            entityInfo.Properties.AddRange(properties);
            entityInfo.PrimaryKeyName = _settingOptions.DefultPrimaryKeyName;
            entityInfo.EntityRemark = entityRemark;
            entityInfo.Interfaces = interfaces.ToArray();
            properties.Select(x => x.GetDtoCode).ToList();


            return entityInfo;
        }
    }
}
